package com.hy.service.impl;


import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hy.common.base.RespResult;
import com.hy.common.base.RespResultCode;
import com.hy.common.consts.CommonConst;
import com.hy.common.consts.RedisConst;
import com.hy.common.excel.UserExcelImport;
import com.hy.common.exception.ImportException;
import com.hy.common.utils.ExcelUtil;
import com.hy.common.utils.CommonUtils;
import com.hy.common.utils.MyObjectUtils;
import com.hy.entity.dto.UpdateUserDTO;
import com.hy.entity.dto.UserSearchDTO;

import com.hy.entity.pojo.HyUser;
import com.hy.entity.pojo.UserRoleConn;
import com.hy.entity.vo.HyUserVO;
import com.hy.entity.vo.OutUserTree;
import com.hy.entity.vo.UserReadVO;
import com.hy.entity.vo.UserListVO;
import com.hy.mapper.HyDepartmentMapper;
import com.hy.mapper.HyUnitMapper;
import com.hy.mapper.HyUserMapper;

import com.hy.mapper.UserRoleConnMapper;
import com.hy.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private HyUserMapper userMapper;


    @Resource
    private SqlSessionFactory sqlSessionFactory;
    @Resource
    private UserRoleConnMapper userRoleConnMapper;
    @Resource
    private HyDepartmentMapper departmentMapper;
    @Resource
    private HyUnitMapper unitMapper;

    @Resource
    private RedisTemplate redisTemplate;
    private  Authentication authentication;

    public  boolean isMember(List<String> roles,String role){
        Iterator<String> iterator = roles.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if (next.equals(role)){
                return true;
            }
        }
        return false;

    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveStuListWithBatch(List<HyUser> data) {
        /**
         * mybatis提供三种sql执行器，分别是SIMPLE（默认）、REUSE、BATCH
         * SIMPLE	默认执行器， 节约服务器资源	每次都要开关Statement
         * REUSE	提升后端接口处理效率	每次一个新sql都缓存，增大JVM内存压力
         * BATCH	专门用于更新插入操作，效率最快	对select 不适用，另外特殊要求，比如限制一次execteBatch的数量时需要写过滤器定制
         */
        // 打开批处理
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
        /*HyPropertyBatchMapper empBatchMapper = session.getMapper(HyPropertyBatchMapper.class);*/
        for (int i = 0,length = data.size(); i < length; i++) {
            HyUser hyUser = data.get(i);
            UserRoleConn conn=new UserRoleConn();
            userMapper.insert(hyUser);
            conn.setUserId(hyUser.getUserId());
            conn.setRoleId(5);
            userRoleConnMapper.insert(conn);
//            empBatchMapper.insert(data.get(i));
            //每1000条提交一次防止内存溢出
            if(i % 10000==9999){
                session.commit();
                session.clearCache();
            }
        }
        session.commit();
        session.clearCache();


    }
    @Override
    public RespResult showUserByDto(UserSearchDTO dto) {
        dto.setDel(1);
        authentication= SecurityContextHolder.getContext().getAuthentication();
        HyUserVO currentUserInfo=(HyUserVO)authentication.getPrincipal();

        List<String> roles = currentUserInfo.getRoles();
        Page<HyUserVO> userVOPage=new Page<>(dto.getCurrentPage(),dto.getPageSize());
        List<HyUserVO> userDetailBySearchDto=null;
        if (isMember(roles,CommonConst.SYSTEM_ROLE_NAME)){
            //系统管理员查询,什么都能查
            userDetailBySearchDto = userMapper.getUserDetailBySearchDto(userVOPage, dto);


        }else if (isMember(roles,CommonConst.UNIT_ROLE_NAME)){
            //单位管理员查询，只能查他自己单位的

            dto.setUnitId(currentUserInfo.getUnitId());
            userDetailBySearchDto = userMapper.getUserDetailBySearchDto(userVOPage, dto);
        }else if (isMember(roles,CommonConst.PROPERTIES_ROLE_NAME)){
            //资产管理员查询
            dto.setUnitId(currentUserInfo.getUnitId());
            userDetailBySearchDto = userMapper.getUserDetailBySearchDto(userVOPage, dto);

        }


        return RespResult.success(userDetailBySearchDto)
                .setCount(Long.valueOf(userVOPage.getTotal()).intValue());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult addUser(UpdateUserDTO userDTO) {

        String phone = userDTO.getPhone();


        authentication= SecurityContextHolder.getContext().getAuthentication();
        HyUserVO currentUserInfo=(HyUserVO)authentication.getPrincipal();
        List<String> roles = currentUserInfo.getRoles();
        HyUser hyUser=new HyUser();
        BeanUtils.copyProperties(userDTO,hyUser);
        hyUser.setDel(1);
        hyUser.setPassword(CommonConst.DEFAULT_PASSWORD);
        Long createDateTime=new Date().getTime()+ CommonConst.GMT_TIME;
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateTime = simpleDateFormat.format(new Date(createDateTime));
        hyUser.setCreateDate(dateTime);
        hyUser.setUpdateDate(dateTime);
        boolean flag=false; //信号量用于违规后的补偿，默认设置为普通员工
        try {
            if (isMember(roles,CommonConst.SYSTEM_ROLE_NAME)){
                //系统管理员什么都角色能加
                //先添加用户
                userMapper.insert(hyUser);
                //再添加角色、必须勾选角色
                for (Integer roleId : userDTO.getRoles()) {
                    UserRoleConn roleConn=new UserRoleConn();
                    roleConn.setUserId(hyUser.getUserId());
                    roleConn.setRoleId(roleId);
                    userRoleConnMapper.insert(roleConn);
                    flag=true;
                }

            }else if (isMember(roles,CommonConst.UNIT_ROLE_NAME)){
                //单位管理员可以设置资产管理员，以及下面的员工

                userMapper.insert(hyUser);
                //筛选掉单位管理员及以上的角色，只允许添加资产管理员和普通员工
                List<Integer> roleIds = userDTO.getRoles();
                for (Integer roleId :roleIds ) {
                    if (roleId==CommonConst.PROPERTIES_ROLE_ID||roleId==CommonConst.COMMON_ROLE_ID||roleId==CommonConst.OUT_USER){
                        UserRoleConn roleConn=new UserRoleConn();
                        roleConn.setUserId(hyUser.getUserId());
                        roleConn.setRoleId(roleId);
                        userRoleConnMapper.insert(roleConn);
                        flag=true;
                    }
                }

            }else if (isMember(roles,CommonConst.PROPERTIES_ROLE_NAME)){

                //资产管理员，可以加普通的员工  无论发信号量什么只能加普通员工
                userMapper.insert(hyUser);
                List<Integer> roleIds = userDTO.getRoles();
                for (Integer roleId :roleIds ) {
                    if (roleId==CommonConst.CHECK_ROLE_ID||roleId==CommonConst.COMMON_ROLE_ID||roleId==CommonConst.OUT_USER){
                        UserRoleConn roleConn=new UserRoleConn();
                        roleConn.setUserId(hyUser.getUserId());
                        roleConn.setRoleId(roleId);
                        userRoleConnMapper.insert(roleConn);
                        flag=true;
                    }
                }
            }
            if (!flag){
                UserRoleConn roleConn=new UserRoleConn();
                roleConn.setUserId(hyUser.getUserId());
                roleConn.setRoleId(5);
                userRoleConnMapper.insert(roleConn);

            }
        }catch (Exception e){
            e.printStackTrace();

            return RespResult.getInstance(RespResultCode.ADD_ERROR);
        }


        return RespResult.getInstance(RespResultCode.Add_SUCCESS);
    }

    public boolean isCheck(List<Integer> roleIds){
        authentication= SecurityContextHolder.getContext().getAuthentication();
        HyUserVO currentUserInfo=(HyUserVO)authentication.getPrincipal();
        List<Integer> currentRoles = currentUserInfo.getRoleIds();
        Set<Integer> currentUserRoleSet=new TreeSet<>();
        Set<Integer> userRoleSet=new TreeSet<>();
        roleIds.stream().forEach(e->  userRoleSet.add(e)  );
        currentRoles.stream().forEach(a-> currentUserRoleSet.add(a));
        Integer []current=new Integer[5];
        Integer []used=new Integer[5];
         currentUserRoleSet.toArray(current);
         userRoleSet.toArray(used);
        return current[0]-used[0]>=0 ? false :true;

    };
    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult updateUser(UpdateUserDTO userDTO) {
        HyUserVO user = userMapper.findHyUserByUsername(userDTO.getUserId());
        String phone = userDTO.getPhone();


        authentication= SecurityContextHolder.getContext().getAuthentication();
        HyUserVO currentUserInfo=(HyUserVO)authentication.getPrincipal();
        List<String> roles = currentUserInfo.getRoles();
        HyUser hyUser=new HyUser();
        Object o = redisTemplate.opsForValue().get(RedisConst.USER_INFO + hyUser.getUserId());
        BeanUtils.copyProperties(userDTO,hyUser);
        Long createDateTime=new Date().getTime()+ CommonConst.GMT_TIME;
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateTime = simpleDateFormat.format(new Date(createDateTime));
        hyUser.setUpdateDate(dateTime);
        boolean flag=false;
        try {
            if (isMember(roles,CommonConst.SYSTEM_ROLE_NAME)){
                //系统管理员什么都角色能修改
                //先修改用户
                userMapper.updateById(hyUser);
                //先判断角色是否需要修改 需要修改再修改。
                if (userDTO.getRoles().size()!=0){
                    LambdaQueryWrapper<UserRoleConn> userRoleConnLambdaQueryWrapper=new LambdaQueryWrapper<>();
                    userRoleConnLambdaQueryWrapper.eq(UserRoleConn::getUserId,hyUser.getUserId());
                    userRoleConnMapper.delete(userRoleConnLambdaQueryWrapper);
                    for (Integer roleId : userDTO.getRoles()) {
                        UserRoleConn roleConn=new UserRoleConn();
                        roleConn.setUserId(hyUser.getUserId());
                        roleConn.setRoleId(roleId);
                        userRoleConnMapper.insert(roleConn);
                        flag=true;
                    }

                    if (o!=null){
                        redisTemplate.delete(RedisConst.USER_INFO+hyUser.getUserId());
                        HyUserVO hyUserByUsername = userMapper.findHyUserByUsername(hyUser.getUserId());
                        redisTemplate.opsForValue().set(RedisConst.USER_INFO+hyUser.getUserId(),hyUserByUsername);
                    }

                }

            }else if (isMember(roles,CommonConst.UNIT_ROLE_NAME)){
                //单位管理员可以设置资产管理员，以及下面的员工
                //先修改用户

                //先判断角色是否需要修改 需要修改再修改。
                if (isCheck(userDTO.getRoles())){
                    if (userDTO.getRoles().size()!=0){
                        userMapper.updateById(hyUser);
                        LambdaQueryWrapper<UserRoleConn> userRoleConnLambdaQueryWrapper=new LambdaQueryWrapper<>();
                        userRoleConnLambdaQueryWrapper.eq(UserRoleConn::getUserId,hyUser.getUserId());
                        userRoleConnMapper.delete(userRoleConnLambdaQueryWrapper);
                        for (Integer roleId : userDTO.getRoles()) {
                            if (roleId==CommonConst.PROPERTIES_ROLE_ID||roleId==CommonConst.COMMON_ROLE_ID){
                                //添加
                                UserRoleConn roleConn=new UserRoleConn();
                                roleConn.setUserId(hyUser.getUserId());
                                roleConn.setRoleId(roleId);
                                userRoleConnMapper.insert(roleConn);
                                flag=true;
                            }
                        }
                        if (!flag){
                            UserRoleConn roleConn=new UserRoleConn();
                            roleConn.setUserId(hyUser.getUserId());
                            roleConn.setRoleId(5);
                            userRoleConnMapper.insert(roleConn);
                        }
                    }

                    if (o!=null){
                        redisTemplate.delete(RedisConst.USER_INFO+hyUser.getUserId());
                        HyUserVO hyUserByUsername = userMapper.findHyUserByUsername(hyUser.getUserId());
                        redisTemplate.opsForValue().set(RedisConst.USER_INFO+hyUser.getUserId(),hyUserByUsername);
                    }
                }else {
                    return RespResult.getInstance(RespResultCode.NO_AUTH);
                }
            }else if (isMember(roles,CommonConst.PROPERTIES_ROLE_NAME)){
                for (Integer roleId : userDTO.getRoles()) {
                    if (roleId==CommonConst.SYSTEM_ROLE_ID||roleId==CommonConst.UNIT_ROLE_ID||
                    roleId==CommonConst.PROPERTIES_ROLE_ID||roleId==CommonConst.CHECK_ROLE_ID){
                        return RespResult.error(500,"非法修改角色");
                    }
                }

                //资产管理员，可以加普通的员工  无论发信号量什么只能加普通员工
                userMapper.updateById(hyUser);
                if (o!=null){
                    redisTemplate.delete(RedisConst.USER_INFO+hyUser.getUserId());
                    HyUserVO hyUserByUsername = userMapper.findHyUserByUsername(hyUser.getUserId());
                    redisTemplate.opsForValue().set(RedisConst.USER_INFO+hyUser.getUserId(),hyUserByUsername);
                }
            }
        }catch (Exception e){
            e.printStackTrace();

            return RespResult.getInstance(RespResultCode.UPDATE_ERROR);
        }


        return RespResult.getInstance(RespResultCode.UPDATE_SUCCESS);


    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult deleteUser(List<Integer> ids) {

            RespResult result;
            if (isCheck(ids)){
                userMapper.deleteBatchIds(ids);
                LambdaUpdateWrapper<UserRoleConn> userRoleConnLambdaUpdateWrapper=new LambdaUpdateWrapper<>();
                userRoleConnLambdaUpdateWrapper.in(UserRoleConn::getUserId,ids);
                userRoleConnMapper.delete(userRoleConnLambdaUpdateWrapper);
                List<String> phoneListByIds = userMapper.getPhoneListByIds(ids);

                result=RespResult.getInstance(RespResultCode.DEL_SUCCESS);
            }else {

                result=RespResult.getInstance(RespResultCode.NO_AUTH);
            }

            return result;
    }

    @Override
    public boolean addTaskUserById(Integer id) {
        LambdaQueryWrapper<UserRoleConn> userRoleConnLambdaQueryWrapper=new LambdaQueryWrapper<>();
        userRoleConnLambdaQueryWrapper.eq(UserRoleConn::getUserId,id).eq(UserRoleConn::getRoleId,4);
        UserRoleConn roleConn = userRoleConnMapper.selectOne(userRoleConnLambdaQueryWrapper);
        if (ObjectUtils.isEmpty(roleConn)){
            UserRoleConn userRoleConn=new UserRoleConn();
            userRoleConn.setUserId(id);
            userRoleConn.setRoleId(4);
            userRoleConnMapper.insert(userRoleConn);
            return true;
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RespResult importExcelBigData(MultipartFile multipartFile, HttpServletRequest request) {
        RespResult r = RespResult.success();

        try{
            long start = System.currentTimeMillis();
            String lock="user_lock";
            UserExcelImport excelImport=new UserExcelImport(this,unitMapper,departmentMapper,userMapper);
            excelImport.setLock(lock);
            Integer sheetNo = Convert.toInt(request.getParameter("sheetNo"), 0);
            ExcelUtil.readExcel(multipartFile.getInputStream(), UserReadVO.class,
                    excelImport, sheetNo);
            Integer count=0;
            Integer i=0;
            synchronized (lock){
                lock.wait();
                count  = excelImport.getRunTimes();
                 i = excelImport.getRealCount().get();
                System.out.println(11);
            }
            if (!i.equals(count)) throw new ImportException("参数有误！");
            log.info("本次导入100w数据，总耗时:{}ms", (System.currentTimeMillis() -start));
            r.setCode(200);
            r.setMessage("导入成功");
            return r;
        } catch (Exception e){
            log.error(e.getMessage(), e);
            throw new ImportException(e.getMessage());
        }

    }

    @Override
    public List<HyUserVO> findList(Map<String, Object> beanToMap) {
        return userMapper.findHyPropertyListByMap(beanToMap);
    }

    @Override
    public List<HyUserVO> listPage(Map<String, Object> queryCondition, Integer pageNo, Integer pageSize) {
        Integer offset = (pageNo - 1) * pageSize;
        Integer rows = pageSize;
        authentication= SecurityContextHolder.getContext().getAuthentication();
        HyUserVO principal = (HyUserVO) authentication.getPrincipal();
        List<String> roles = principal.getRoles();
        queryCondition.put("offset",offset);
        queryCondition.put("rows",rows);
        List<HyUserVO> hyUsers=null;
        if(CommonUtils.isMember(roles, CommonConst.SYSTEM_ROLE_NAME)){

            hyUsers= userMapper.findHyPropertyListByMap(queryCondition);
        }else {
            queryCondition.put("unitId",principal.getUnitId());
            hyUsers=userMapper.findHyPropertyListByMap(queryCondition);
        }
        return hyUsers;
    }

    @Override
    public RespResult getUserByDept(Integer deptId) {

        List<HyUser> hyUsers = userMapper.selectList(new LambdaQueryWrapper<HyUser>().and(hyUserLambdaQueryWrapper ->
                hyUserLambdaQueryWrapper.eq(HyUser::getDepartmentId, deptId)));
      List<UserListVO> userListVOS=new ArrayList<>();
        Optional.ofNullable(hyUsers)
                .filter(MyObjectUtils::isNotBlank)
                .ifPresent(e->
                        e.stream().forEach(u->{
                        UserListVO userListVO =new UserListVO();
                        userListVO.setId(u.getUserId());
                        userListVO.setLabel(u.getUsername());
                        userListVOS.add(userListVO);
                    }));
        UserListVO userListVO =new UserListVO();
        userListVO.setId(CommonConst.DEFAULT_ID);
        userListVO.setLabel("暂无");
        userListVOS.add(userListVO);
        return RespResult.success(userListVOS);
    }

    @Override
    public RespResult getOutUserTree() {
        HyUserVO currentUser = CommonUtils.getCurrentUser();
        List<HyUserVO> userVOS= userMapper.selectOutUsers(currentUser.getUnitId());
        OutUserTree outUserTree=new OutUserTree();
        outUserTree.setId(1000);
        outUserTree.setLabel("编外人员");
        List<OutUserTree> outUserTrees=new ArrayList<>();
        userVOS.forEach(e->{
            OutUserTree outUserTree01=new OutUserTree();
            outUserTree01.setId(e.getUserId());
            outUserTree01.setLabel(e.getUsername());
            outUserTrees.add(outUserTree01);
        });
        outUserTree.setChildren(outUserTrees);
        return RespResult.success(outUserTree);
    }
}
